# import pandas as pd
# import numpy as np
# from sklearn.preprocessing import MinMaxScaler, RobustScaler
#
# def normalize_indicators(df, method='robust', cv_threshold=1.0):
#     """
#     对指标数据进行归一化处理，并返回每个指标采用的归一化方法。
#
#     参数：
#         df: 原始DataFrame，包括元数据与指标列。
#         method: 归一化方法，'minmax' | 'robust' | 'dynamic'。
#         cv_threshold: 动态归一化中用于判断的变异系数阈值。
#
#     返回：
#         df_scaled: 归一化后的DataFrame
#         method_dict: 每个指标采用的归一化方法（仅在 dynamic 模式下返回）
#     """
#     meta_cols = ['序号', '国名En', '国名Ch', '年份']
#     indicator_cols = [col for col in df.columns if col not in meta_cols]
#
#     df_scaled = df.copy()
#     method_dict = {}
#
#     if method in ['minmax', 'robust']:
#         scaler = MinMaxScaler() if method == 'minmax' else RobustScaler()
#         scaled_data = scaler.fit_transform(df[indicator_cols])
#         df_scaled = pd.concat([df[meta_cols].reset_index(drop=True),
#                                pd.DataFrame(scaled_data, columns=indicator_cols)], axis=1)
#         method_dict = {col: method for col in indicator_cols}
#
#     elif method == 'dynamic':
#         for col in indicator_cols:
#             series = df[col]
#             if series.mean() == 0:
#                 cv = 0
#             else:
#                 cv = series.std() / series.mean()
#
#             if cv > cv_threshold:
#                 transformed = np.log1p(series)
#                 scaler = MinMaxScaler()
#                 method_used = 'log+minmax'
#             elif cv > 0.5:
#                 transformed = series
#                 scaler = RobustScaler()
#                 method_used = 'robust'
#             else:
#                 transformed = series
#                 scaler = MinMaxScaler()
#                 method_used = 'minmax'
#
#             df_scaled[col] = scaler.fit_transform(transformed.values.reshape(-1, 1))
#             method_dict[col] = method_used
#
#     else:
#         raise ValueError("method must be 'minmax', 'robust' or 'dynamic'")
#
#     return df_scaled, method_dict

"""
    更新：补充注释 & 函数说明
"""
import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler, RobustScaler, StandardScaler

def normalize_indicators(df, method='robust', cv_threshold=1.0):
    """
    对指标数据进行归一化处理，并返回每个指标采用的归一化方法。

    参数：
        df (DataFrame): 原始指标数据，包含元数据列和指标列。
        method (str): 归一化方法，可选值：
            - 'minmax': 最小-最大归一化，将每列缩放到 [0, 1] 区间；
            - 'robust': 稳健归一化，对异常值不敏感，使用中位数和四分位数缩放；
            - 'dynamic': 动态选择归一化方法，根据变异系数 CV 自动选择 log+minmax、robust 或 minmax；
            - 'zscore': Z-score 标准化，将每列转换为均值 0、标准差 1。
        cv_threshold (float): 动态归一化的 CV 阈值，当 CV 超过该值时采用对数转换（仅当 method='dynamic' 时有效）。

    返回：
        df_scaled (DataFrame): 归一化后的指标数据（保留原始元数据列）。
        method_dict (dict): 每个指标列使用的归一化方法映射。当 method='dynamic' 时各列方法可能不同；其他模式下各列使用同一方法。
    """
    meta_cols = ['序号', '国名En', '国名Ch', '年份']  # 元数据列名称
    indicator_cols = [col for col in df.columns if col not in meta_cols]  # 指标列名称列表

    df_scaled = df.copy()          # 拷贝原始数据，用于存储归一化结果
    method_dict = {}              # 字典记录每个指标列采用的归一化方法

    if method in ['minmax', 'robust', 'zscore']:
        # 统一归一化：对所有指标列使用同一种缩放方法
        if method == 'minmax':
            scaler = MinMaxScaler()
            # Min-Max 归一化：将每列数值线性缩放到 [0, 1] 区间
        elif method == 'robust':
            scaler = RobustScaler()
            # Robust 归一化：使用中位数和四分位数缩放，降低异常值影响
        else:
            scaler = StandardScaler()
            # Z-score 归一化：将每列转换为均值 0、标准差 1
        # 对所有指标列整体拟合并转换
        scaled_data = scaler.fit_transform(df[indicator_cols])
        # 将元数据列与缩放后的指标列合并
        df_scaled = pd.concat([df[meta_cols].reset_index(drop=True),
                               pd.DataFrame(scaled_data, columns=indicator_cols)], axis=1)
        # 为所有指标记录相同的归一化方法
        method_dict = {col: method for col in indicator_cols}

    elif method == 'dynamic':
        # 动态归一化：根据每个指标的变异系数 CV 挑选不同归一化策略
        for col in indicator_cols:
            series = df[col]
            # 计算该指标的变异系数 CV = 标准差 / 均值（避免均值为 0 时除零）
            if series.mean() == 0:
                cv = 0
            else:
                cv = series.std() / series.mean()
            # 根据 CV 大小选择归一化方法
            if cv > cv_threshold:
                # CV 高于阈值：波动剧烈，先取对数平滑后再进行 Min-Max 归一化
                transformed = np.log1p(series)    # log1p 处理，保证包含0时计算稳定
                scaler = MinMaxScaler()
                method_used = 'log+minmax'
            elif cv > 0.5:
                # CV 中等（介于0.5和阈值之间）：使用 RobustScaler 进行稳健归一化
                transformed = series
                scaler = RobustScaler()
                method_used = 'robust'
            else:
                # CV 较小：直接使用 Min-Max 归一化
                transformed = series
                scaler = MinMaxScaler()
                method_used = 'minmax'
            # 对该指标列应用对应的缩放变换（需先reshape为二维）
            df_scaled[col] = scaler.fit_transform(transformed.values.reshape(-1, 1))
            # 记录该指标列实际采用的方法
            method_dict[col] = method_used

    else:
        # 输入method不支持，抛出异常
        raise ValueError("不支持的归一化方法，请使用 'minmax', 'robust', 'dynamic' 或 'zscore'")

    return df_scaled, method_dict
